package au.com.langdale.gwt.client.remote;

import java.util.AbstractList;
import java.util.ArrayList;

import au.com.langdale.gwt.client.model.ModelContainer;
import au.com.langdale.gwt.client.model.ModelListener;
import au.com.langdale.gwt.client.model.ModelListenerCollection;

import com.google.gwt.user.client.History;
import com.google.gwt.user.client.HistoryListener;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * A List that represents a subsequence of a potentially larger 
 * remote sequence of model objects (ie a database table).<p>
 * 
 *  RemoteList manages an internal Batch and provides access to its
 *  contents via the List interface.<p> 
 *  
 *  RemoteList also provides commands and properties to navigate
 *  the larger remote sequence by asynchronously fetching new
 *  Batches.<p>
 *  
 *  It emits events to registered ModelListeners that
 *  signal changes in its properties or contents.<p>
 *  
 *  Concrete RemoteList implementations must provide the pull()
 *  method to actually fetch new contents. The pull()
 *  method should arrange to call back the RemoteList via its 
 *  AsyncCallback interface or one of the overloads of onSuccess()
 *  on completion.<p>
 * 
 * @author adv
 *
 */
public abstract class RemoteList extends AbstractList implements ModelContainer, HistoryListener, AsyncCallback {
	
	protected Batch batch = new Batch();
	protected ModelListenerCollection clients = new ModelListenerCollection();
	private int quota, pending, pendingFirst;
	
	public RemoteList() {
		batch.first = parseAnchor();
		
		History.addHistoryListener(this);
	}
	
	// List methods:
	
	public Object get(int ix) {
		return batch.contents.get(ix);
	}

	public int size() {
		if( batch.contents == null)
			return 0;
		return batch.contents.size();
	}

	// AsyncCallback methods:
	
	public void onFailure(Throwable caught) {
		String msg = "Failed to access data";
		if (caught != null)
			msg += ": " + caught.getMessage();
		Window.alert(msg);
		// TODO: what else?
	}

	public void onSuccess(Object result) {
		onSuccess((Batch) result);
	}

	public void onSuccess(int requestFirst, int requestQuota, ArrayList result) {
		onSuccess(new Batch(result, requestFirst, requestFirst));
	}

	public void onSuccess(Batch result) {
		batch = result;
		pending--;
		makeAnchor(batch.first);
		clients.fireContentsChange(this);
		clients.fireChange(this);
	}
	
	// Event methods:

	public void addModelListener(ModelListener listener) {
		clients.add(listener);
	}

	public void removeModelListener(ModelListener listener) {
		clients.remove(listener);
	}

	// manage the page anchor:
	
	public void onHistoryChanged(String historyToken) {
		jump(parseAnchor());
	}
	
	private void makeAnchor(int startRow) {
		// TODO Auto-generated method stub
		
	}

	protected int parseAnchor() {
		String token = History.getToken();
		return 0;
	}

	// Navigation commands:
	
	public void first() {
		jump(0);
	}

	public void next() {
		jump( batch.first + quota );
	}

	public void prev() {
		jump( Math.max(0, batch.first - quota));
	}

	public void jump(int request) {
		if( pending > 0 && request != pendingFirst || pending == 0 && request != batch.first) {
			dispatch(request);
		}
	}

	public void refresh() {
		dispatch(batch.first);
	}
	
	// properties:

	public int getFirst() {
		return batch.first;
	}
	
	public boolean hasMore() {
		return size() >= batch.quota;
	}

	public int getQuota() {
		return quota;
	}

	public int getPending() {
		return pending;
	}
	
	public void setQuota(int rowCount) {
		quota = rowCount;
		clients.fireChange(this);
	}

	// Initiate async request:

	private void dispatch(int request) {
		pending += 1;
		pendingFirst = request;
		pull(request, quota);
		clients.fireChange(this);
	}

	abstract protected void pull(int requestFirst, int requestQuota );
}
